﻿using CANCommunication;
using DataManage;
using ServiceLayerManagement;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace BMSPlatform.DeviceCommunication
{
    /// <summary>
    /// 设备链接
    /// <para>2.数据发送</para>
    /// </summary>
    public class DeviceConnected
    {
        /// <summary>
        /// 接收缓冲区的数据量
        /// </summary>
        public static uint RecvBufferNum;
        /// <summary>
        /// CAN接收数据类
        /// </summary>
        public static CANRecvdata CANReceivedData;

        #region CAN打开→获取CAN设备类型→初始化→链接 | CAN关闭
        /// <summary>
        /// CAN设备打开
        /// </summary>
        public static RespResult<string> OpenDevice(PlatformConfig config)
        {
            RespResult<string> respResult = new RespResult<string>();
            try
            {
                uint deviceType = 0;
                if (config.CANType is ZLGCANType zLGCANType)
                {
                    deviceType = (uint)zLGCANType;
                }
                else
                {
                    respResult.Code = -1;
                    respResult.Message = "设备类型转换异常";
                    return respResult;
                }

                GlobalData.Device_Handle = MethodInvoke.ZCAN_OpenDevice(deviceType, (uint)config.CANChannel, 0);
                if (0 == (int)GlobalData.Device_Handle)
                {
                    respResult.Code = -2;
                    respResult.Message = "打开设备失败,请检查设备类型和设备索引号是否正确";
                    return respResult;
                }
                OnInitCAN(config);
            }
            catch (Exception ex)
            {
                //CAN复位
                if (0 != (int)GlobalData.Device_Handle) MethodInvoke.ZCAN_ResetCAN(GlobalData.Device_Handle);
                respResult.Code = -3;
                respResult.Message = ex.Message;
            }
            return respResult;
        }
        /// <summary>
        /// 波特率设置
        /// </summary>
        public static void OnInitCAN(PlatformConfig config)
        {
            bool canfd = false;
            if (config.CANType is ZLGCANType canType)
            {
                if (canType.IsUSBCANFD() || canType.IsUSBCANFD_PCIE()) canfd = true;

                //设置协议类型,默认CAN类型
                if (canType.IsUSBCANFD200U() || canType.IsUSBCANFDPCIE400U() || canType.IsUSBCANPCIE200UEX())
                    MethodInvoke.ZCAN_SetValue(GlobalData.Device_Handle, "0/set_device_recv_merge", Encoding.ASCII.GetBytes("0"));

                // 设置波特率
                if (!SetBautRate(canfd ? "canfd_abit_baud_rate" : "baud_rate", config)) throw new Exception("设置波特率失败");

            }
            SetCANInit(config.CANType, canfd, config);
            OnCANStart();
        }
        /// <summary>
        /// 初始化配置
        /// </summary>
        private static void SetCANInit(object obj, bool fdEnable, PlatformConfig platformConfig)
        {
            tagZLG_CHANNEL_INIT_CONFIG config = new tagZLG_CHANNEL_INIT_CONFIG();
            config.can_Type = (uint)ZLGCANProtocolType.CAN;
            config.canConfig.filter = 0;
            config.canConfig.accCode = 0;
            config.canConfig.accMask = 0xFFFFFFFF;
            config.canConfig.mode = 0;//默认正常模式
            config.canFDConfig.mode = 0;

            if (obj is ZLGCANType zlgType)
            {
                if (zlgType.IsUSBCANFD()) config.canConfig.accMask = 0;

                if (fdEnable) config.can_Type = (uint)ZLGCANProtocolType.CANFD;
            }

            IntPtr pConfig = Marshal.AllocHGlobal(Marshal.SizeOf(config));
            Marshal.StructureToPtr(config, pConfig, true);
            GlobalData.Channel_Handle = MethodInvoke.ZCAN_InitCAN(GlobalData.Device_Handle, (uint)platformConfig.CANChannel, pConfig);
            Marshal.FreeHGlobal(pConfig);

            if (0 == (int)GlobalData.Channel_Handle) throw new Exception("初始化CAN失败");
        }
        /// <summary>
        /// 开启CAN通道
        /// </summary>
        public static void OnCANStart()
        {
            if (MethodInvoke.ZCAN_StartCAN(GlobalData.Channel_Handle) != Collection.STATUS_OK) throw new Exception("启动CAN失败");

            if (null == CANReceivedData)
            {
                CANReceivedData = new CANRecvdata();
                CANReceivedData.SetChannelHandle(GlobalData.Channel_Handle);
                CANReceivedData.SetStart(true);
                CANReceivedData.RecvCANData += AddData;
                CANReceivedData.RecvFDData += AddData;
            }
            else
            {
                CANReceivedData.SetChannelHandle(GlobalData.Channel_Handle);
            }
            GlobalData.DeviceClosed += MethodInvoke.ZCAN_CloseDevice;
            CANReceivedData.RecvBufferNum += UpdateRecvBufferNum;
            Device.DeviceInit();
        }
        /// <summary>
        /// 设置波特率
        /// </summary>
        private static bool SetBautRate(string path, PlatformConfig config)
        {
            return
                (1 == MethodInvoke.ZCAN_SetValue(GlobalData.Device_Handle, $"{config.CANChannel}/{path}", Encoding.ASCII.GetBytes(Collection.KBaudRate[config.BautRate].ToString())));
        }
        /// <summary>
        /// 关闭CAN设备
        /// </summary>
        public static void CloseDevice()
        {
            MethodInvoke.ZCAN_ResetCAN(GlobalData.Channel_Handle);
            MethodInvoke.ZCAN_ClearBuffer(GlobalData.Channel_Handle);
            MethodInvoke.ZCAN_CloseDevice(GlobalData.Device_Handle);
        }
        #endregion

        #region 数据发送管理
        public static void AddData(object obj, uint len, ZLGCANProtocolType type)
        {
            if (len <= 0 || obj == null) return;

            DataFrames dataFrames = new DataFrames();
            tagZCAN_Receive_Data[] tagZCAN_Receive_Data = new tagZCAN_Receive_Data[0];
            tagZCAN_ReceiveFD_Data[] tagZCAN_ReceiveFD_Data = new tagZCAN_ReceiveFD_Data[0];
            if (type == ZLGCANProtocolType.CAN)
            {
                tagZCAN_Receive_Data = obj as tagZCAN_Receive_Data[];
            }
            else if (type == ZLGCANProtocolType.CANFD)
            {
                tagZCAN_ReceiveFD_Data = obj as tagZCAN_ReceiveFD_Data[];
            }
            for (int i = 0; i < len; i++)
            {
               
                if (type == ZLGCANProtocolType.CAN)
                {
                    if (i <= tagZCAN_Receive_Data.Length) dataFrames = tagZCAN_Receive_Data[i].ToDataFrames();
                }
                else if (type == ZLGCANProtocolType.CANFD)
                {
                    if (i <= tagZCAN_ReceiveFD_Data.Length) dataFrames = tagZCAN_ReceiveFD_Data[i].ToDataFrames();
                }

                dataFrames.FrameFormat = (byte)(IsEFF(dataFrames.CANId) ? 1 : 0);
                dataFrames.FrameType = (byte)(IsRTR(dataFrames.CANId) ? 1 : 0);
                GlobalData.ReceviedData.Enqueue(SerializeHelper.DeepCopy(dataFrames));
            }
        }

        public static uint MakeCanId(uint id, int eff, int rtr, int err)//1:extend frame 0:standard frame
        {
            uint ueff = (uint)(!!(Convert.ToBoolean(eff)) ? 1 : 0);
            uint urtr = (uint)(!!(Convert.ToBoolean(rtr)) ? 1 : 0);
            uint uerr = (uint)(!!(Convert.ToBoolean(err)) ? 1 : 0);
            return id | ueff << 31 | urtr << 30 | uerr << 29;
        }

        /// <summary>
        /// 帧格式
        /// <para>0：标准帧</para>
        /// <para>1：扩展帧</para>
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool IsEFF(uint id)
        {
            return !!Convert.ToBoolean((id & Collection.CAN_EFF_FLAG));
        }

        /// <summary>
        /// 帧类型
        /// <para>0：数据帧</para>
        /// <para>1：远程帧</para>
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public static bool IsRTR(uint id)
        {
            return !!Convert.ToBoolean((id & Collection.CAN_RTR_FLAG));
        }
        /// <summary>
        /// 发送数据
        /// </summary>
        public static uint SendData(DataFrames dataFrames)
        {
            if (dataFrames == null) return uint.MinValue;

            DataFrames data = SerializeHelper.DeepCopy(dataFrames);
            uint result = 1;
            tagZCAN_Transmit_Data tagZCAN_Transmit;
            if (data.CANType == ZLGCANProtocolType.CAN)
            {
                data.CANId = MakeCanId(data.CANId, data.FrameFormat, 0, 0);
                tagZCAN_Transmit = data.ToZCAN_Trabsmit_Data();
                //建立非托管资源的指针
                IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(tagZCAN_Transmit));
                //将对象送到非托管区
                Marshal.StructureToPtr(tagZCAN_Transmit, ptr, true);
                result = MethodInvoke.ZCAN_Transmit(GlobalData.Channel_Handle, ptr, 1);
                //释放非托管资源区
                Marshal.FreeHGlobal(ptr);
            }

            if (result != 1) GetErrorInfo();

            return result;
        }

        /// <summary>
        /// 获取CAN错误信息
        /// <para>1.发送指令失败</para>
        /// </summary>
        private static List<CANChannelErrorInfo> GetErrorInfo()
        {
            List<CANChannelErrorInfo> channelErrorInfo = new List<CANChannelErrorInfo>();
            CANChannelErrorInfo errorInfo = new CANChannelErrorInfo();
            tagZCAN_CHANNEL_ERR_INFO tagChannelErrInfo = new tagZCAN_CHANNEL_ERR_INFO();
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(tagChannelErrInfo));
            Marshal.StructureToPtr(tagChannelErrInfo, ptr, true);
            if (MethodInvoke.ZCAN_ReadChannelErrInfo(GlobalData.Channel_Handle, ptr) != Collection.STATUS_OK
                || tagChannelErrInfo.errCode == 0)
            {//获取错误信息失败，检查设备是否在线
                if (MethodInvoke.ZCAN_IsDeviceOnLine(GlobalData.Device_Handle) != Collection.STATUS_ONLINE)
                {//CAN通讯异常，获取错误信息失败
                    channelErrorInfo.Add(new CANChannelErrorInfo()
                    {
                        ErrCode = uint.MinValue,
                        Meassage = Collection.ZLGErrorType[errorInfo.ErrCode]
                    });
                    return channelErrorInfo;
                }

            }
            errorInfo.ErrCode = tagChannelErrInfo.errCode;
            errorInfo.Meassage = Collection.ZLGErrorType[errorInfo.ErrCode];
            channelErrorInfo.Add(errorInfo);
            Marshal.FreeHGlobal(ptr);

            return channelErrorInfo;
        }
        #endregion

        #region 其他
        private static void UpdateRecvBufferNum(uint u)
        {
            RecvBufferNum = u;
        }
        #endregion
    }
}
